﻿using System.Collections.Generic;
using System.Xml;
using System;

namespace SixthSense
{
    class Config
    {
        private string _ConfigFile;
        private Profile _CurrentProfile = new Profile();
        private ConfigOptions _CurrentOptions = new ConfigOptions();
        private XmlDocument doc = new XmlDocument();

        public ProfileList Profiles = new ProfileList();

        public Config()
        {
        }

        public Config(string ConfigFile)
        {
            _ConfigFile = ConfigFile;
            Load();
        }

        ~Config()
        {
            CommitToDisk(_ConfigFile);
        }

        public void CommitToDisk()
        {
            CommitToDisk(_ConfigFile);
        }

        public void CommitToDisk(string ConfigFile)
        {
            UpdateDoc();

            try
            {
                doc.Save(ConfigFile);
            }
            catch (XmlException e)
            {
                string msg = e.Message;
            }
        }

        private XmlNode MakeOptionNode(string Name, string Value)
        {
            XmlNode node = doc.CreateNode(XmlNodeType.Element, "option", null);
            XmlAttribute attribName = doc.CreateAttribute("name");
            attribName.Value = Name;
            node.Attributes.Append(attribName);

            XmlAttribute attribValue = doc.CreateAttribute("value");
            attribValue.Value = Value;
            node.Attributes.Append(attribValue);
            return node;
        }

        private XmlNode MakeExclusionNode(string Exclusion)
        {
            XmlNode node = doc.CreateNode(XmlNodeType.Element, "exclude", null);
            XmlAttribute attribName = doc.CreateAttribute("name");
            attribName.Value = Exclusion;
            node.Attributes.Append(attribName);

            return node;
        }

        private XmlNode MakeItemNode(AlertInfo Item)
        {
            // name/substring, type, sound, enable
            XmlNode node = doc.CreateNode(XmlNodeType.Element, "item", null);

            if (Item.Exact)
            {
                XmlAttribute attribName = doc.CreateAttribute("name");
                attribName.Value = Item.Name;
                node.Attributes.Append(attribName);
            }
            else
            {
                XmlAttribute attribName = doc.CreateAttribute("substring");
                attribName.Value = Item.Name;
                node.Attributes.Append(attribName);
            }

            XmlAttribute attribType = doc.CreateAttribute("type");
            attribType.Value = Item.Type;
            node.Attributes.Append(attribType);
            
            XmlAttribute attribSound = doc.CreateAttribute("sound");
            attribSound.Value = Item.Sound;
            node.Attributes.Append(attribSound);

            XmlAttribute attribEnable = doc.CreateAttribute("enable");
            attribEnable.Value = Item.Enable.ToString();
            node.Attributes.Append(attribEnable);

            // substrings can also have exclusions
            if (Item.Exclude.Count > 0)
            {
                foreach (string exclusion in Item.Exclude)
                {
                    node.AppendChild(MakeExclusionNode(exclusion));
                }
            }

            return node;
        }

        private XmlNode MakeMaterialNode(MaterialInfo Material)
        {
            XmlNode node = doc.CreateNode(XmlNodeType.Element, "material", null);
            XmlAttribute attribName = doc.CreateAttribute("name");
            attribName.Value = Material.Name;
            node.Attributes.Append(attribName);

            XmlAttribute attribId = doc.CreateAttribute("id");
            attribId.Value = Material.ID.ToString();
            node.Attributes.Append(attribId);

            XmlAttribute attribWork = doc.CreateAttribute("workmanship");
            attribWork.Value = Material.Workmanship.ToString();
            node.Attributes.Append(attribWork);
            return node;
        }

        private void ClearXMLProfiles()
        {
            XmlNodeList xmlProfiles = doc.GetElementsByTagName("profile");

            //start with wiping the nodes that are there so we can lay them down from memory
            foreach (XmlNode xmlProfile in xmlProfiles)
            {
                xmlProfile.RemoveAll();
            }
        }

        private void UpdateDoc()
        {
            // at some point, I should be updating this on the fly as I change the config items
            // for now, just going to wipe and load

            XmlElement element = doc.DocumentElement;
            element.RemoveAll();

            // Now go through memory and dump out each profile
            foreach (Profile profile in Profiles)
            {
                // make the new profile node
                XmlNode xmlProfile = doc.CreateNode(XmlNodeType.Element, "profile", null);

                XmlAttribute profileName = doc.CreateAttribute("name");
                profileName.Value = profile.Name;
                xmlProfile.Attributes.Append(profileName);

                // make the items subgroup for this profile
                XmlNode xmlItems = doc.CreateNode(XmlNodeType.Element, "items", null);
                xmlProfile.AppendChild(xmlItems);

                foreach (AlertInfo item in profile.Alerts)
                {
                    // item should now represent each row in the items xml for this profile
                    xmlItems.AppendChild(MakeItemNode(item));
                }

                // make the materials subgroup for this profile
                XmlNode xmlMaterials = doc.CreateNode(XmlNodeType.Element, "materials", null);
                xmlProfile.AppendChild(xmlMaterials);

                foreach (MaterialInfo material in profile.Materials)
                {
                    // material should represent each material listed in this profile
                    xmlMaterials.AppendChild(MakeMaterialNode(material));
                }

                element.AppendChild(xmlProfile);
            }

            // options
            XmlNode options = doc.CreateNode(XmlNodeType.Element, "options", null);

            options.AppendChild(MakeOptionNode("popup", _CurrentOptions.GetValue("popup")));
            options.AppendChild(MakeOptionNode("clipboard", _CurrentOptions.GetValue("clipboard")));
            options.AppendChild(MakeOptionNode("sound", _CurrentOptions.GetValue("sound")));
            options.AppendChild(MakeOptionNode("chatwindow", _CurrentOptions.GetValue("chatwindow")));
            options.AppendChild(MakeOptionNode("relativecoords", _CurrentOptions.GetValue("relativecoords")));
            options.AppendChild(MakeOptionNode("trackvalue", _CurrentOptions.GetValue("trackvalue")));
            options.AppendChild(MakeOptionNode("trackratio", _CurrentOptions.GetValue("trackratio")));
            options.AppendChild(MakeOptionNode("minvalue", _CurrentOptions.GetValue("minvalue")));
            options.AppendChild(MakeOptionNode("ratio", _CurrentOptions.GetValue("ratio")));
            options.AppendChild(MakeOptionNode("trackportals", _CurrentOptions.GetValue("trackportals")));
            options.AppendChild(MakeOptionNode("ignorecorpses", _CurrentOptions.GetValue("ignorecorpses")));
            options.AppendChild(MakeOptionNode("ignorehooks", _CurrentOptions.GetValue("ignorehooks")));
            options.AppendChild(MakeOptionNode("activeprofile", _CurrentOptions.GetValue("activeprofile")));

            element.AppendChild(options);
        }

        public Profile CurrentProfile
        {
            get
            {
                return _CurrentProfile;
            }
            set
            {
                _CurrentProfile = value;
            }
        }

        public ConfigOptions CurrentOptions
        {
            get
            {
                return _CurrentOptions;
            }
            set
            {
                _CurrentOptions = value;
            }
        }

        public bool Load()
        {
            try
            {
                doc.Load(_ConfigFile);
            }
            catch (XmlException e)
            {
                string msg = e.Message;
                return false;
            }

            // parse document into memory           

            XmlElement element = doc.DocumentElement;
            XmlNodeList profiles = doc.GetElementsByTagName("profile");

            // can have multiple profiles.
            // each profile can have items and materials
            foreach (XmlNode profile in profiles)
            {
                Profile newProfile = new Profile();
                newProfile.Name = profile.Attributes["name"].Value; ;

                XmlNode items = profile.SelectSingleNode("items");

                foreach (XmlNode item in items)
                {
                    AlertInfo info = new AlertInfo();

                    XmlNode nodeName = item.Attributes.GetNamedItem("name");
                    if (nodeName != null)
                    {
                        // If the name field is there, this is an exact string match
                        info.Exact = true;
                        info.Name = nodeName.Value;
                        info.Text = nodeName.Value;
                    }
                    else
                    {
                        XmlNode nodeSubstring = item.Attributes.GetNamedItem("substring");
                        if (nodeSubstring != null)
                        {
                            // If the substring field is there, this is a substring (still case sensitive though) that we are looking for.
                            info.Exact = false;
                            info.Name = nodeSubstring.Value;
                            info.Text = nodeSubstring.Value;

                            // since this is a substring, lets also check to see if there are excluded substrings
                            XmlNode ExcludeNode = item.SelectSingleNode("exclude");

                            foreach (XmlNode exclude in item)
                            {
                                info.Exclude.Add(exclude.Attributes["name"].Value);
                            }
                        }
                    }

                    if (info.Name == "")
                    {
                        // If neither is there, someone needs a spanking;
                        return false;
                    }

                    XmlNode nodeType = item.Attributes.GetNamedItem("type");
                    if (nodeType != null)
                    {
                        info.Type = nodeType.Value;
                    }
                    else
                    {
                        // if there is no type, we can't do anything. corrupt config file
                        return false;
                    }

                    XmlNode nodeSound = item.Attributes.GetNamedItem("sound");
                    if (nodeSound != null)
                    {
                        // sound may or may not be there. doesnt matter if its not. it should work if it is
                        info.Sound = nodeSound.Value;
                    }

                    XmlNode nodeEnabled = item.Attributes.GetNamedItem("enabled");
                    if (nodeEnabled != null)
                    {
                        // enabled is set to true by default in the class.  Only change that if enabled is set to false.  It may not exist
                        info.Enable = false;
                    }

                    newProfile.Alerts.Add(info);
                }

                XmlNode materials = profile.SelectSingleNode("materials");

                foreach (XmlNode material in materials)
                {
                    // maerial has: name, id, workmanship

                    MaterialInfo mat = new MaterialInfo();

                    XmlNode nodeName = material.Attributes.GetNamedItem("name");
                    if (nodeName != null)
                    {
                        mat.Name = nodeName.Value;
                    }

                    XmlNode nodeId = material.Attributes.GetNamedItem("id");
                    if (nodeId != null)
                    {
                        mat.ID = Convert.ToInt32(nodeId.Value);
                    }

                    XmlNode nodeWork = material.Attributes.GetNamedItem("workmanship");
                    if (nodeWork != null)
                    {
                        mat.Workmanship = Convert.ToInt32(nodeWork.Value);
                    }

                    newProfile.Materials.Add(mat);
                }

                Profiles.Add(newProfile);
            }

            // Load Options, which is global
            XmlNode options = element.SelectSingleNode("options");

            foreach (XmlNode option in options)
            {
                string sName = "";
                string sValue = "false";

                XmlNode nodeName = option.Attributes.GetNamedItem("name");
                if (nodeName != null)
                {
                    sName = nodeName.Value;
                }

                XmlNode nodeValue = option.Attributes.GetNamedItem("value");
                if (nodeValue != null)
                {
                    sValue = nodeValue.Value;
                }

                _CurrentOptions.SetValue(sName, sValue);
            }

            if (_CurrentOptions.ActiveProfile != "")
            {
                _CurrentProfile = Profiles.FindByName(_CurrentOptions.ActiveProfile);
            }

            if (_CurrentProfile == null)
            {
                // set a default profile
                _CurrentProfile = Profiles.FindByName("default");
            }

            return true;
        }

        public bool LoadDefaultProfile()
        {
            Profile p = new Profile();
            p = Profiles.FindByName("Default");

            if (p != null)
            {
                _CurrentProfile = p;
                return true;
            }

            return false;
        }

        public bool Load(string ConfigFile)
        {
            _ConfigFile = ConfigFile;
            return Load();
        }

        public void Personalize(string CharacterName)
        {
            foreach (Profile profile in Profiles)
            {
                foreach (AlertInfo alert in profile.Alerts)
                {
                    alert.Name.Replace("%player%", CharacterName);
                }
            }
        }
    }
}
